package com.chuanke.ckfamily.service.finance.impl;

import com.alibaba.fastjson.JSONObject;
import com.chuanke.ckfamily.dao.base.CkUtil;
import com.chuanke.ckfamily.dao.finance.GainPacketDao;
import com.chuanke.ckfamily.dao.finance.model.Bill;
import com.chuanke.ckfamily.dao.finance.model.GainPacket;
import com.chuanke.ckfamily.dao.finance.model.RedPacket;
import com.chuanke.ckfamily.dao.social.model.Family;
import com.chuanke.ckfamily.dao.social.model.FamilyMember;
import com.chuanke.ckfamily.dao.user.model.User;
import com.chuanke.ckfamily.service.base.FailCode;
import com.chuanke.ckfamily.service.common.CacheService;
import com.chuanke.ckfamily.service.common.RSAService;
import com.chuanke.ckfamily.service.common.WyService;
import com.chuanke.ckfamily.service.common.impl.RedisLockUtil;
import com.chuanke.ckfamily.service.common.model.RSAKey;
import com.chuanke.ckfamily.service.constant.BillType;
import com.chuanke.ckfamily.service.constant.MsgEunm;
import com.chuanke.ckfamily.service.constant.RedPacketStatus;
import com.chuanke.ckfamily.service.constant.RedPacketType;
import com.chuanke.ckfamily.service.exception.CFException;
import com.chuanke.ckfamily.service.finance.BillService;
import com.chuanke.ckfamily.service.finance.GainPacketService;
import com.chuanke.ckfamily.service.finance.RedPacketService;
import com.chuanke.ckfamily.service.finance.UserDayIncomeService;
import com.chuanke.ckfamily.service.finance.vo.GainPacketPo;
import com.chuanke.ckfamily.service.finance.vo.RedPacketInfo;
import com.chuanke.ckfamily.service.finance.vo.RedPacketRecod;
import com.chuanke.ckfamily.service.social.FamilyMemberService;
import com.chuanke.ckfamily.service.social.FamilyService;
import com.chuanke.ckfamily.service.user.UserService;
import com.clusterswisdom.kid.server.libary.util.DateUtil;
import com.weijie.core.BaseBiz;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.sql.Timestamp;
import java.util.*;

@Service
@Transactional(rollbackFor=Exception.class)
public class GainPacketServiceImpl extends BaseBiz<GainPacket, GainPacketDao> implements GainPacketService {

    private static final Logger log = LoggerFactory.getLogger(GainPacketServiceImpl.class);

    public static final int DAY_SPAN = 24 * 60 * 60 * 1000;

    // 测试环境给 10分钟
    //public static final int DAY_SPAN_TEST = 10 * 60 * 1000;

    @Autowired
    private UserService userService;

    @Autowired
    private FamilyService familyService;
    
    
    @Autowired
    private FamilyMemberService familyMemberService;

    @Autowired
    private RedPacketService redPacketService;

    @Autowired
    private CacheService cacheService;

    @Autowired
    private BillService billService;

    @Autowired
    private RSAService rsaService;

    @Autowired
    private WyService wyService;

    @Autowired
    private UserDayIncomeService userDayIncomeService;

    @Override
    public boolean hasGainedPacket(Long uid, Long pid) throws CFException {
        Long begin = System.currentTimeMillis();
        if (!isActivited(uid)) {
            throw new CFException(FailCode.REDPACKET_NOROLE, "您今天还没有分享文章\n不能领取红包！");
        }
        log.info("hasGainedPacket ============>uid={},pid={}, activited = {}",  uid,pid,(System.currentTimeMillis() - begin));

        boolean hasGained = cacheService.get(CacheService.KEY_PREFIX.packetPickup_.name() + pid + "_" + uid) != null;
        if (hasGained) {
            return true;
        }
        log.info("hasGainedPacket ============>uid={},pid={}, hasGained = {}",  uid,pid,(System.currentTimeMillis() - begin));

       if (!hasEngouthPacket(pid)) {
            throw new CFException(FailCode.REDPACKET_EMPLTY);
        }
        log.info("hasGainedPacket ============>uid={},pid={}, hasEngouthPacket = {}",  uid,pid,(System.currentTimeMillis() - begin));
        
        FamilyMember member = hasFamily(uid);
        if ( member == null ) {
            throw new CFException(FailCode.FAMILY_MEMBER_NOT_EXIST);
        }
        log.info("hasGainedPacket ============>uid={},pid={}, findFamilyByUserId = {}", uid,pid, (System.currentTimeMillis() - begin));

        RedPacket redPacket = redPacketService.findById(pid);
        if (redPacket == null || redPacket.getFid().longValue() != member.getFamily_id().longValue()) {
            throw new CFException(FailCode.REDPACKET_ONINFAMILY);
        }
        if( redPacket.getStatus() != null  &&  redPacket.getStatus().intValue() == RedPacketStatus.EMPTY.getStatus()){
        	 throw new CFException(FailCode.REDPACKET_EMPLTY);
        }
        
        log.info("hasGainedPacket ============>uid={},pid={}, findPacket = {}",  uid,pid,(System.currentTimeMillis() - begin));

        if (redPacket.getStatus() == null
                || redPacket.getStatus().intValue() != RedPacketStatus.NORMAL.getStatus()
                || System.currentTimeMillis() - redPacket.getCreate_time().getTime() > DAY_SPAN) {
            throw new CFException(FailCode.REDPACKET_TIMEOUT);
        }
        log.info("hasGainedPacket ============>uid={},pid={}, check status = {}",  uid,pid,(System.currentTimeMillis() - begin));

        cacheService.set(CacheService.KEY_PREFIX.packetCheckup_.name() + pid + "_" + uid, true);
        return false;
    }

    private FamilyMember hasFamily(Long uid) {
    	FamilyMember member = (FamilyMember) cacheService.get(CacheService.KEY_PREFIX.familyMember_.name()+uid);
		if (member == null) {
			member =  familyMemberService.findByUserId(uid);
			cacheService.set(CacheService.KEY_PREFIX.familyMember_.name() + uid, member);
    	}
		return member;
	}

	@Override
    public GainPacket getAPacket(Long uid, Long pid) throws CFException {
        if (cacheService.get(CacheService.KEY_PREFIX.packetCheckup_.name() + pid + "_" + uid) == null) {
            log.debug("getAPacket========》没有check 过 。uid={},rid={}", uid,pid);
            throw new CFException(FailCode.REDPACKET_EMPLTY);
        }
        cacheService.delete(CacheService.KEY_PREFIX.packetCheckup_.name() + pid + "_" + uid);
        
        Long onePacketMoney = (Long)cacheService.rightPop(CacheService.KEY_PREFIX.packet_money_queue_.name() + pid);
        if (onePacketMoney == null) {
            cacheService.set(CacheService.KEY_PREFIX.packet_empty_.name() + pid, 0l);
            throw new CFException(FailCode.REDPACKET_EMPLTY);
        }

        long redpacketSize = cacheService.listSize(CacheService.KEY_PREFIX.packet_money_queue_.name() + pid);
        GainPacket gainPacket = new GainPacket();
        if (redpacketSize == 0) {
        	cacheService.set(CacheService.KEY_PREFIX.packet_empty_.name() + pid, 0l);
            gainPacket.setIs_last(true);
        }
        gainPacket.setUid(uid);
        gainPacket.setGain_time(new Timestamp(System.currentTimeMillis()));
        gainPacket.setPid(pid);
        gainPacket.setPrice(onePacketMoney);
        cacheService.set(CacheService.KEY_PREFIX.packetPickup_.name() + pid + "_" + uid, gainPacket);

        /*添加到队列*/
        cacheService.leftPush(CacheService.KEY_PREFIX.gain_packet_queue.name(), gainPacket);
        return gainPacket;
    }

    private void pushGainQueue(User pickUser, GainPacket gainPacket) {
        GainPacketPo po = new GainPacketPo();
        po.setNick_name(pickUser.getNick_name());
        po.setPic(pickUser.getPic());
        po.setKing(false);
        po.setPrice(CkUtil.formatMoney(gainPacket.getPrice()));
        po.setTime(DateFormatUtils.format(gainPacket.getGain_time(), "yyyy-MM-dd HH:mm:ss"));
        cacheService.leftPush(CacheService.KEY_PREFIX.packet_gain_queue_.name() + gainPacket.getPid(), po);
    }

    public boolean synizeCacheToDb() {
        Long size = cacheService.listSize(CacheService.KEY_PREFIX.gain_packet_queue.name());
        if (size == null || size == 0) {
            return false;
        }

        Map<Long, RedPacket> updatePacketMap = new HashMap<>(10);
        List<GainPacket> megeredList = new LinkedList<>();
        List<Bill> billsList = new ArrayList<>(size.intValue());
        for (int i = 0; i < size && i < 50; i++) {
            GainPacket gainPacket = (GainPacket)cacheService.rightPop(CacheService.KEY_PREFIX.gain_packet_queue.name());
            megeredList.add(gainPacket);

            User pickUser = null;
            try {
                pickUser = userService.findById(gainPacket.getUid());
            } catch (CFException e) {
                e.printStackTrace();
                continue;
            }

            pushGainQueue(pickUser, gainPacket);
            /*发送领取tip*/
            try {
                RedPacket redPacket = redPacketService.findById(gainPacket.getPid());
                sendRedpacketTip(pickUser, redPacket);
            } catch (CFException e) {
                e.printStackTrace();
            }

            //计算红包累积操作
            Long pid = gainPacket.getPid();
            RedPacket update = updatePacketMap.get(pid);
            if (update == null) {
                update = new RedPacket();
                update.setLeft_total(0);
                update.setLeft_money(0l);
            }
            update.setLeft_total(update.getLeft_total() + 1);
            update.setLeft_money(update.getLeft_money() + gainPacket.getPrice());
            updatePacketMap.put(pid, update);

            RedPacket packet = redPacketService.findById(pid);
            Bill bill = new Bill();
            bill.setMoney(gainPacket.getPrice());
            bill.setUid(gainPacket.getUid());
            bill.setCreate_time(new Timestamp(System.currentTimeMillis()));
            bill.setOptype(packet.getType().intValue() == RedPacketType.FAMILY.getType()
                    ? BillType.FAMILYPACKET.getType() : BillType.HEADPACKET.getType());
            bill.setFlag(0);
            bill.setFnote("领取红包");
            billsList.add(bill);
        }

        baseDao.addList(megeredList);
        billService.addBills(billsList);

        Iterator<Map.Entry<Long, RedPacket>> iterator = updatePacketMap.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<Long, RedPacket> entry = iterator.next();
            RedisLockUtil lockUtil = null;
            try {
                lockUtil = cacheService.getLock(CacheService.KEY_PREFIX.packet_.name() + entry.getKey());
                RedPacket redPacket = redPacketService.findById(entry.getKey());

                redPacket.setLeft_total(redPacket.getLeft_total() - entry.getValue().getLeft_total());
                redPacket.setLeft_money(redPacket.getLeft_money() - entry.getValue().getLeft_money());
                if (redPacket.getLeft_total() == 0) {
                    redPacket.setStatus(RedPacketStatus.EMPTY.getStatus());
                }
                redPacketService.update(redPacket);

            } catch (Exception e) {
                e.printStackTrace();

            } finally {
                if (lockUtil != null) {
                    lockUtil.unlock();
                }

            }
        }

        return size > 50;
    }

    public GainPacket getGainPacketByUser(Long uid, Long pid) throws CFException {
        userService.findById(uid);
        RedPacket redPacket = redPacketService.findById(pid);
        if (redPacket == null) {
            throw new CFException(FailCode.PARAMS_ERROR);
        }

        Map<String, Object> param = new HashMap<>(2);
        param.put("uid", uid);
        param.put("pid", pid);
        return baseDao.findGainRecord(param);
    }

    @Override
    public RedPacketInfo getRecords(Long uid, Long packetId) throws CFException {
    	Long begin = System.currentTimeMillis();
        Family family = familyService.findFamilyByUserId(uid);
        if (family == null) {
            throw new CFException(FailCode.APP, "用户不在家族中");
        }
        log.info("get_packet_records findFamilyByUserId================>uid={},pid={},used = {}", uid,packetId,(System.currentTimeMillis() - begin));

        RedPacket redPacket = redPacketService.findById(packetId);
        if (redPacket == null || redPacket.getFid().longValue() != family.getId().longValue()) {
            throw new CFException(FailCode.APP, "只能查看家族的红包");
        }
        log.info("get_packet_records findById================>uid={},pid={},used = {}", uid,packetId,(System.currentTimeMillis() - begin));

        User fowner = userService.findById(family.getOwner_user_id());
        RedPacketInfo packetInfo = new RedPacketInfo();
        if (redPacket.getType() == RedPacketType.FAMILY.getType()) {
            packetInfo.setIcon(family.getFicon());
        } else {
            packetInfo.setIcon(fowner.getPic());

        }

        packetInfo.setTitle(redPacket.getRnote());
        packetInfo.setType(redPacket.getType());
        packetInfo.setTotalNum(redPacket.getAll_total());
        packetInfo.setPickedNum(redPacket.getAll_total() - redPacket.getLeft_total());
        packetInfo.setTotalMoney(CkUtil.formatMoney(redPacket.getP_money()));
        packetInfo.setPickedMoney(CkUtil.formatMoney(redPacket.getP_money() - redPacket.getLeft_money()));

        GainPacket gainPacket = getGainPacketByUser(uid, packetId);
        log.info("get_packet_records getGainPacketByUser================>uid={},pid={},used = {}", uid,packetId,(System.currentTimeMillis() - begin));
        
        if (gainPacket != null) {
            packetInfo.setPickUpTime(DateFormatUtils.format(gainPacket.getGain_time(), "yyyy-MM-dd HH:mm:ss"));
            packetInfo.setMoney(CkUtil.formatMoney(gainPacket.getPrice()));

        } else {
            packetInfo.setPickUpTime(DateFormatUtils.format(redPacket.getCreate_time(), "yyyy-MM-dd HH:mm:ss"));
            packetInfo.setMoney("");

        }

        packetInfo.setRecords(getGainRecords(uid, redPacket));
        log.info("get_packet_records getGainRecords================>uid={},pid={},used = {}", uid,packetId,(System.currentTimeMillis() - begin));
        return packetInfo;
    }

    private List<GainPacketPo> getGainRecords(Long uid, RedPacket redPacket) throws CFException {
        long size = cacheService.listSize(CacheService.KEY_PREFIX.packet_gain_queue_.name() + redPacket.getId());
        List<GainPacketPo> packetPoList = (List<GainPacketPo>)cacheService.getLists(CacheService.KEY_PREFIX.packet_gain_queue_.name() + redPacket.getId(), 0l, size);
        if (packetPoList != null && packetPoList.size() > 0) {
            if (redPacket.getStatus().intValue() == RedPacketStatus.EMPTY.getStatus()) {
                GainPacketPo kpo = null;
                double money = -1;
                for (GainPacketPo packet: packetPoList) {
                    Double pmoney = Double.parseDouble(packet.getPrice());
                    if (kpo == null || money < pmoney) {
                        kpo = packet;
                        money = pmoney;
                    }
                }
                kpo.setKing(true);
            }
            return packetPoList;

        } else {
            List<GainPacket> packetList = baseDao.findRecords(redPacket.getId());
            List<GainPacketPo> poList = new ArrayList<>(packetList.size());
            GainPacketPo kpo = null;
            long money = -1;
            for (GainPacket packet: packetList) {
                GainPacketPo po = new GainPacketPo();
                User pickUser = userService.findProxyUser(uid, packet.getUid());
                po.setNick_name(pickUser.getNick_name());
                po.setPic(pickUser.getPic());
                po.setKing(false);
                po.setPrice(CkUtil.formatMoney(packet.getPrice()));
                po.setTime(DateFormatUtils.format(packet.getGain_time(), "yyyy-MM-dd HH:mm:ss"));

                poList.add(po);
                if (kpo == null || money < packet.getPrice()) {
                    kpo = po;
                    money = packet.getPrice();
                }
            }

            if (kpo != null && redPacket.getStatus().intValue() == RedPacketStatus.EMPTY.getStatus()) {
                kpo.setKing(true);
            }

            return poList;

        }
    }

    @Override
    public List<RedPacketRecod> getHistory(Long uid, int p) {
        Map<String, Object> param = new HashMap<>(3);
        param.put("uid", uid);
        param.put("start", p * 30);
        param.put("length", 30);

        List<Map> historyList = baseDao.findHistory(param);
        List<RedPacketRecod> redPacketRecods = new ArrayList<>(historyList.size());
        for (Map itemMap: historyList) {
            Long price = (Long)itemMap.get("price");
            Timestamp gainTime = (Timestamp)itemMap.get("gain_time");
            Integer type = (Integer)itemMap.get("type");
            String name = (String)itemMap.get("name");

            RedPacketRecod redPacketRecod = new RedPacketRecod();
            if (type.intValue() == RedPacketType.FAMILY.getType()) {
                redPacketRecod.setFamily(name);
            } else {
                redPacketRecod.setFamily((String)itemMap.get("nick_name"));
            }

            redPacketRecod.setPid((Long)itemMap.get("pid"));
            redPacketRecod.setMoney(CkUtil.formatMoney(price));
            redPacketRecod.setStatus("已领取");
            redPacketRecod.setTime(DateFormatUtils.format(gainTime, "MM-dd"));
            redPacketRecod.setType(RedPacketType.getTypeTitle(type));
            redPacketRecods.add(redPacketRecod);
        }

        return redPacketRecods;
    }

    @Override
    public Long countHistory(Long uid) {
        return baseDao.countHistory(uid);
    }

    @Override
    public String sumHistory(Long uid) {
        Long sumMoney = baseDao.sumHistory(uid);
        return CkUtil.formatMoney(sumMoney);
    }

    public GainPacket getCanGet(Long uid, Long rid) throws CFException {
        RedPacket redPacket = redPacketService.findById(rid);
        if (redPacket.getLeft_total().intValue() < 1) {
            log.debug("getAPacket========》红包已领完。uid={},rid={}", uid, rid);
            throw new CFException(FailCode.REDPACKET_EMPLTY);
        }

        return findGainRecord(uid, rid);
    }

    private void sendRedpacketTip(User user, RedPacket redPacket) throws CFException {
        RSAKey rsaKey = rsaService.getRSAKeyMsg();
        JSONObject bodyJson = new JSONObject();
        bodyJson.put("msg", "领取了" + RedPacketType.getTypeTitle(redPacket.getType()));
        bodyJson.put("type", MsgEunm.redpacket_pickup_msg.getMsgtype());
        bodyJson.put("key", rsaKey.getPublicKey());
        bodyJson.put("sender_id", user.getId());
        if (redPacket.getType().intValue() == RedPacketType.FAMILY.getType()) {
            Family family = familyService.findById(redPacket.getFid());
            bodyJson.put("sender_pic", family.getFicon());

        } else {
            bodyJson.put("sender_pic", user.getPic());

        }

        String nick_name = user.getNick_name();
        if (StringUtils.isNotEmpty(nick_name) && nick_name.length() > 10) {
            nick_name = nick_name.substring(0, 10) + "...";
        }

        JSONObject redpacketTip  = new JSONObject(2);
        redpacketTip.put("uno", user.getUser_no());
        redpacketTip.put("nick_name", nick_name);
        redpacketTip.put("pid", redPacket.getId());

        String extStr = rsaService.encryptByPrivateKey(redpacketTip.toString(),rsaKey.getKeyPair().getPrivate());
        bodyJson.put("redpacketTip", extStr);

        Family family = familyService.findFamilyByUserId(user.getId());
        wyService.sendMsgForHongBao(user.getUser_no(), family.getTid(), bodyJson);
        log.info("领取红包消息========================>id={},fid={},pid={}",user.getId(),redPacket.getFid(),redPacket.getId());
    }

    private boolean isActivited(Long uid) {
        Long shareTime = (Long)cacheService.get(CacheService.KEY_PREFIX.user_share_time_.name() + uid);
        if (shareTime == null) {
            boolean isShared = userDayIncomeService.isDayUserShared(uid, CkUtil.getNowDayStr());
            if (isShared) {
                cacheService.set(CacheService.KEY_PREFIX.user_share_time_.name() + uid, System.currentTimeMillis());
            }
            return isShared;

        } else {
            boolean shared = shareTime > DateUtil.getDayBegin().getTime();
            if (!shared) {
                log.info("is un shared uid={}, shareTime={}, dayBegin={}", uid, shareTime, DateUtil.getDayBegin().getTime());
                shared = userDayIncomeService.isDayUserShared(uid, CkUtil.getNowDayStr());
                if (shared) {
                    cacheService.set(CacheService.KEY_PREFIX.user_share_time_.name() + uid, System.currentTimeMillis());
                }
            }
            return shared;

        }
    }

    private boolean hasEngouthPacket(Long pid) {
        return cacheService.get(CacheService.KEY_PREFIX.packet_empty_.name() + pid) == null;
    }

    private GainPacket findGainRecord(Long uid, Long pid) {
        GainPacket gainPacket = (GainPacket) cacheService.get(CacheService.KEY_PREFIX.packetPickup_.name() + pid + "_" + uid);
        if (gainPacket == null) {
            Map<String, Object> param = new HashMap<>(2);
            param.put("uid", uid);
            param.put("pid", pid);

            return baseDao.findGainRecord(param);

        } else {
            return gainPacket;

        }
    }
}